package com.yb.core.aop;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSONObject;
import com.yb.core.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Copyright (C), 2022-2022, 姚兵
 * Author: 32210
 * Date: 2022/12/9 22:15
 * FileName: WebLogAspect
 * Description:
 */

@Aspect
@Configuration
@EnableAspectJAutoProxy
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class WebLogAspect {

    LocalVariableTableParameterNameDiscoverer localVariableTable = new LocalVariableTableParameterNameDiscoverer();

    @Pointcut("execution(public * com.yb.*..controller.*.*(..))")
    public void controller(){

    }
    @Pointcut("" +
            "@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "||" +
            "@annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "||" +
            "@annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "||" +
            "@annotation(org.springframework.web.bind.annotation.PutMapping)" +
            "||" +
            "@annotation(org.springframework.web.bind.annotation.DeleteMapping)"+
            "||" +
            "@annotation(org.springframework.web.bind.annotation.PatchMapping)"
    )
    public void annotations(){

    }
    @Pointcut("@annotation(com.yb.core.annotation.WebLogIgnore)")
    public void ignore(){

    }
    @Around("controller() && annotations() && !ignore()")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        LocalDateTime startTime = LocalDateTime.now();
        //使用ServletRequestAttributes请求上下文获取方法更多
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        Arrays.stream(signature.getMethod().getAnnotations()).forEach(item->{log.info("annotation:{}",item);});
        //使用数组来获取参数
        Object[] args = pjp.getArgs();
        String[] parameterNames = localVariableTable.getParameterNames(signature.getMethod());
        Map<String, Object> paramMap = new LinkedHashMap<>();
        for(int index=0;index<args.length;index++){
            if( !(args[index] instanceof ServletRequest || args[index] instanceof ServletResponse || args[index] instanceof MultipartFile)){
                paramMap.put(parameterNames[index],args[index]);
            }
        }

        //执行函数前打印日志
        Object result=null;
        String sessionId = IdUtil.simpleUUID();
        try {

            log.info("[requestId:{}]={uri:{},ip:{},requestMethod:{},method:{}.{}(),params:{}}",sessionId,request.getRequestURI(),RequestUtil.getIpAddr(request),request.getMethod(),className, methodName, JSONObject.toJSONString(paramMap));
//            log.info("请求入参：URL:{},IP:{},requestMethod:{},{}.{}(),args：{}",request.getRequestURI(), RequestUtil.getIpAddr(request),request.getMethod(),className, methodName, JSONObject.toJSONString(paramMap));
            //调用整个目标函数执行
            result = pjp.proceed();
            return result;
        }catch (Exception e){

            throw e;
        }finally {
            //执行函数后打印日志
            LocalDateTime endTime = LocalDateTime.now();

            Duration duration = Duration.between(startTime,endTime);
            log.info("[requestId:{}]={timespan:{}ms,method:{}.{}(),result:{}}",sessionId,duration.toMillis(), className, methodName, JSONObject.toJSONString(result));

        }

    }
}
